package cn.xiaolang.common.utils;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Dean
 * @date 2022-01-15
 */
public class DnHttpClients {

    private static final Logger log = LoggerFactory.getLogger(DnHttpClients.class);
    private static final String HTTP_ENCODING = "UTF-8";
    private static final CloseableHttpClient httpClient;

    static {
        // 初始化线程池
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(3000).setConnectionRequestTimeout(1000).setSocketTimeout(4000).setExpectContinueEnabled(true).build();
        PoolingHttpClientConnectionManager pccm = new PoolingHttpClientConnectionManager();
        pccm.setMaxTotal(300); // 连接池最大并发连接数
        pccm.setDefaultMaxPerRoute(50); // 单路由最大并发数
        HttpRequestRetryHandler retryHandler = (exception, executionCount, context) -> {
            // 重试1次,从1开始
            if (executionCount > 1) {
                return false;
            }
            if (exception instanceof NoHttpResponseException) {
                log.info("NoHttpResponseException has retry request:{},executionCount:{}", context, executionCount);
                return true;
            } else if (exception instanceof SocketException) {
                log.info("SocketException has retry request:{},executionCount:{}", context, executionCount);
                return true;
            }
            return false;
        };
        httpClient = HttpClients.custom().setConnectionManager(pccm).setDefaultRequestConfig(requestConfig).setRetryHandler(retryHandler).build();
    }

    /**
     * 执行GET请求，响应状态码为200返回内容，如果是其他状态码返回null
     *
     * @param url request URL
     * @return request result string
     */
    public static String doGet(String url) {
        HttpGet httpGet = new HttpGet(url);

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            if (200 == response.getStatusLine().getStatusCode()) {
                return EntityUtils.toString(response.getEntity(), HTTP_ENCODING);
            }
        } catch (IOException e) {
            log.error("do GET request ex:", e);
        }
        return null;
    }

    /**
     * /**
     * 执行GET请求，响应状态码为200返回内容，如果是其他状态码返回null
     *
     * @param url request URL
     * @param queryString request queryString
     * @return request result string
     */
    public static String doGet(String url, Map<String, String> queryString) {
        try {
            URIBuilder builder = new URIBuilder(url);
            if (null != queryString) {
                for (Map.Entry<String, String> entry : queryString.entrySet()) {
                    builder.addParameter(entry.getKey(), entry.getValue());
                }
            }
            return doGet(builder.build().toString());
        } catch (URISyntaxException e) {
            log.error("do GET URL builder ex:", e);
        }
        return null;
    }

    /**
     * post form data
     *
     * @param url request URL
     * @param formParams form data
     * @return HttpResult
     */
    public static HttpResult doPost(String url, Map<String, String> formParams) {
        HttpPost httpPost = new HttpPost(url);
        if (null != formParams) {
            // 设置post参数
            List<NameValuePair> parameters = new ArrayList<>(0);
            for (Map.Entry<String, String> entry : formParams.entrySet()) {
                parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            // 构造一个form表单式的实体
            UrlEncodedFormEntity formEntity = null;
            try {
                formEntity = new UrlEncodedFormEntity(parameters, HTTP_ENCODING);
            } catch (UnsupportedEncodingException e) {
                log.error("set form data ex:", e);
            }
            // 将请求实体设置到httpPost对象中
            httpPost.setEntity(formEntity);
        }
        return doPost(httpPost);
    }

    /**
     * post json
     *
     * @param url request URL
     * @param json json request body
     * @return HttpResult : when exception return null
     */
    public static HttpResult doPost(String url, String json) {
        // 创建http POST请求
        HttpPost httpPost = new HttpPost(url);
        if (null != json) {
            // 设置json数据并且指定内容类型为json
            StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(stringEntity);
        }
        return doPost(httpPost);
    }

    private static HttpResult doPost(HttpPost httpPost) {
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            // 执行请求
            return new HttpResult(response.getStatusLine().getStatusCode(),
                    EntityUtils.toString(response.getEntity(), HTTP_ENCODING));
        } catch (IOException e) {
            log.error("do POST request ex:", e);
        }
        return null;
    }
}


@Data
@AllArgsConstructor
@NoArgsConstructor
class HttpResult {

    private Integer code;

    private String data;

}
